/**
 * 
 */
package org.wicket.jbpm.engine.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.db.GraphSession;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.Comment;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.taskmgmt.def.Task;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springmodules.workflow.jbpm31.JbpmCallback;
import org.springmodules.workflow.jbpm31.JbpmTemplate;
import org.wicket.jbpm.engine.exception.WicketJbpmException;
import org.wicket.jbpm.engine.spring.JbpmLogEngine;
import org.wicket.jbpm.engine.utils.JbpmEngineUtils;
import org.wicket.jbpm.engine.workflow.WfTask;

/**
 * @author Emmanuel Nollase - emanux created: Jun 1, 2009 - 6:45:08 PM
 */
public class JbpmLogEngineImpl extends BPMEngine implements JbpmLogEngine
{

    protected JbpmTemplate jbpmTemplate;

    public void setJbpmTemplate(JbpmTemplate jbpmTemplate)
    {
	this.jbpmTemplate = jbpmTemplate;
    }

    // Note: jBPM query which is not provided out-of-the-box
    // TODO: Check jBPM 3.2 and get this implemented in jBPM
    private final static String COMPLETED_TASKS_QUERY = "select ti "
    + " from org.jbpm.taskmgmt.exe.TaskInstance ti " + " join ti.pooledActors pooledActor "
    + " where pooledActor.actorId = :swimlaneActorId " + "	and ti.actorId is null "
    + " and ti.end is not null " + " and ti.isOpen = false";

    private final static String TASK_COMMENTS = "select cm "
    + " from org.jbpm.graph.exe.Comment cm " + " where cm.taskInstance = :taskId ";

    private final static String TASKINSTANCE_PROCESSINSTANCE = " select ti "
    + " from org.jbpm.taskmgmt.exe.TaskInstance ti "
    + " where ti.token.processInstance = :processInstance ";

    /*
     * "select ti " + "from org.jbpm.taskmgmt.exe.TaskInstance as ti " +
     * "where ti.actorId = :actorId " + "and ti.isOpen = false " +
     * "and ti.end is not null";
     */
    /*
     * (non-Javadoc)
     * 
     * @see com.ccti.workflow.jbpm.engine.JbpmLogEngine#findAllCompletedTask()
     */
    public List<WfTask> findAllCompletedTask()
    {

	return null;

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.ccti.workflow.jbpm.engine.JbpmLogEngine#findAllCompletedTaskByActor
     * (java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public List<WfTask> findAllCompletedTaskByActor(final String actorId) throws WicketJbpmException
    {
	try
	{
	    return (List<WfTask>) jbpmTemplate.execute(new JbpmCallback() {

		public Object doInJbpm(JbpmContext context)
		{
		    List<TaskInstance> tasks;
		    final Query query = context.getSession().createQuery(COMPLETED_TASKS_QUERY);
		    query.setString("swimlaneActorId", actorId);
		    tasks = (List<TaskInstance>) query.list();

		    final List<WfTask> workflowTasks = new ArrayList<WfTask>(tasks.size());
		    for (TaskInstance task : tasks)
		    {
			WfTask wfTask = JbpmEngineUtils.createWorkflowTask(engineId, task);
			workflowTasks.add(wfTask);
		    }
		    return workflowTasks;
		}
	    });
	}
	catch (JbpmException e)
	{
	    throw new WicketJbpmException("Failed to get assigned task for '" + actorId + "'", e);
	}
    }

    /*
     * (non-Javadoc)
     * @see org.wicket.jbpm.engine.spring.JbpmLogEngine#findAllComments(java.lang.Long)
     */
    @SuppressWarnings("unchecked")
    public List<Comment> findAllComments(final Long taskId) throws WicketJbpmException
    {
	try
	{
	    return (List<Comment>) jbpmTemplate.execute(new JbpmCallback() {

		public Object doInJbpm(JbpmContext context)
		{
		    List<Comment> comments;
		    final Query query = context.getSession().createQuery(TASK_COMMENTS);
		    query.setLong("taskId", taskId);
		    comments = (List<Comment>) query.list();

		    return comments;
		}
	    });
	}
	catch (JbpmException e)
	{
	    throw new WicketJbpmException("Failed to get comment task for '" + taskId + "'", e);
	}
    }

    /*
     * (non-Javadoc)
     * @see org.wicket.jbpm.engine.spring.JbpmLogEngine#findAllOpenProcessInstance()
     */
    @SuppressWarnings("unchecked")
    public List<ProcessInstance> findAllOpenProcessInstance() 
    {
	try
	{
	    return (List<ProcessInstance>) jbpmTemplate.execute(new JbpmCallback() {

		public Object doInJbpm(JbpmContext context) throws JbpmException
		{
		    final Session session = context.getSession();
		    // final Query query =
		    // session.createQuery(" from org.jbpm.graph.exe.ProcessInstance");
		    final Query query = session.getNamedQuery("OpenProcessInstance");

		    return (List<ProcessInstance>) query.list();
		}

	    });
	}
	catch (Exception e)
	{
	    throw new WicketJbpmException("No process instance get", e);
	}
    }

    /*
     * (non-Javadoc)
     * @see org.wicket.jbpm.engine.spring.JbpmLogEngine#findAllCloseProcessInstance()
     */
    @SuppressWarnings("unchecked")
    public List<ProcessInstance> findAllCloseProcessInstance()
    {
	try
	{
	    return (List<ProcessInstance>) jbpmTemplate.execute(new JbpmCallback() {

		public Object doInJbpm(JbpmContext context) throws JbpmException
		{
		    final Session session = context.getSession();
		    // final Query query =
		    // session.createQuery(" from org.jbpm.graph.exe.ProcessInstance");
		    final Query query = session.getNamedQuery("CloseProcessInstance");

		    return (List<ProcessInstance>) query.list();
		}

	    });
	}
	catch (Exception e)
	{
	    throw new WicketJbpmException("No process instance get", e);
	}
    }

    /*
     * (non-Javadoc)
     * @see org.wicket.jbpm.engine.spring.JbpmLogEngine#findAllTaskByProcessInstance(org.jbpm.graph.exe.ProcessInstance)
     */
    @SuppressWarnings("unchecked")
    public List<WfTask> findAllTaskByProcessInstance(final ProcessInstance instance) throws WicketJbpmException
    {
	try
	{
	    return (List<WfTask>) jbpmTemplate.execute(new JbpmCallback() {

		public Object doInJbpm(JbpmContext context) throws JbpmException
		{
		    final List<WfTask> tmpTasks = new ArrayList<WfTask>();
		    final Session session = context.getSession();
		    final Query query = session.createQuery(TASKINSTANCE_PROCESSINSTANCE);
		    query.setEntity("processInstance", instance);
		    final List<TaskInstance> tasks = query.list();

		    for (TaskInstance task : tasks)
		    {
			final WfTask wftask = JbpmEngineUtils.createWorkflowTask(engineId, task);
			tmpTasks.add(wftask);
		    }
		    return tmpTasks;
		}
	    });
	}
	catch (Exception e)
	{
	    throw new WicketJbpmException("No task instance get", e);
	}
    }

    @SuppressWarnings("unchecked")
    public List<Task> findNextTasksByProcessInstance(final ProcessInstance process) throws WicketJbpmException
    {
		try {
			return (List<Task>) jbpmTemplate.execute(new JbpmCallback() {

				public Object doInJbpm(JbpmContext context)
						throws JbpmException {
					
					final long processDefId = process.getProcessDefinition().getId();
					
					final GraphSession sess = context.getGraphSession();
					final ProcessDefinition def = sess.getProcessDefinition(processDefId);
					final Map<String, Task> task = def.getTaskMgmtDefinition().getTasks();
					final Set<String> taskSet = task.keySet();
					
					final Session session = context.getSession();
					final Query query = session.getNamedQuery("findNextTasksByProcessInstance");
					query.setEntity("processInstance", process);
					final List<String> taskInstance = query.list();
					
					return JbpmEngineUtils.getNextTasks(taskSet, taskInstance, task);
				}
			});
		} catch (JbpmException e) {
			throw new WicketJbpmException("No task instance get", e);
		}
    }
}
